// =============================================================================
//  STATS - Statistical Analysis Tools, v.0.01
//  Copyright (C) 2012  Marco Vettigli
// -----------------------------------------------------------------------------
//
// This program  is free software:  you can  redistribute it and/or  modify it
// under the terms of the  GNU General Public License as published by the Free
// Software Foundation,  either version 3 of the License,  or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY;  without even  the  implied  warranty  of  MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License  for
// more details.
//
// You  should  have received a copy of the  GNU General Public License  along
// with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// =============================================================================
//
// SDOUBLEAXIS.H
// The class represents a axis of continuous values with variable properties
// to be used in the construction of complex graphs.
//
// Author: Marco Vettigli
//
// =============================================================================

#ifndef SDOUBLEAXIS_H
#define SDOUBLEAXIS_H

// == include directives =======================================================

#include <QWidget>
#include <QFont>
#include <QMouseEvent>
#include "sdoubles.h"

// == class definitions ========================================================

namespace StatS {

class SDoubleAxis : public QWidget
{

    Q_OBJECT

public:

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::Orientation
    //
    // Orientation enum defines orientation states both for axis and labels.
    // Possible states are 'Horizontal' and 'Vertical'
    //
    // -------------------------------------------------------------------------
    enum Orientation {Vertical, Horizontal };

// == constructors and destructors =============================================

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::SDoubleAxis(QWidget*)
    //
    // Default constructor for SDoubleAxis class. Aurgument 'parent' defines
    // parent widget of the object.
    //
    // -------------------------------------------------------------------------
    explicit SDoubleAxis(QWidget* parent = 0);

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::~SDoubleAxis()
    //
    // Default destructor for SDoubleAxis class.
    //
    // -------------------------------------------------------------------------
    ~SDoubleAxis();


// == getters ==================================================================

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::size()
    //
    // Returns the size in pixels of the axis. The value doesn't include the
    // full width (for horizontal ones) or height (for vertical) of the axis.
    //
    // -------------------------------------------------------------------------
    int size() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::min()
    //
    // Returns the minimum value diplayed on the axis.
    //
    // -------------------------------------------------------------------------
    double min() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::max()
    //
    // Returns the maximum value diplayed on the axis.
    //
    // -------------------------------------------------------------------------
    double max() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::range()
    //
    // Returns the value range diplayed on the axis.
    //
    // -------------------------------------------------------------------------
    double range() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::increment()
    //
    // Returns the value increment diplayed on the axis as major ticks.
    //
    // -------------------------------------------------------------------------
    int increment() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::minorTicks()
    //
    // Returns the number of minor ticks contained in a major one.
    //
    // -------------------------------------------------------------------------
    int minorTicks() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::title()
    //
    // Returns the title of the axis.
    //
    // -------------------------------------------------------------------------
    QString title() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::titleFont()
    //
    // Returns the font used to render the title of the axis.
    //
    // -------------------------------------------------------------------------
    QFont titleFont() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::axisOrientation()
    //
    // Returns the orientation of the axis.
    //
    // -------------------------------------------------------------------------
    Orientation axisOrientation() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::labelOrientation()
    //
    // Returns the orientation of labels text.
    //
    // -------------------------------------------------------------------------
    Orientation labelOrientation() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::labelFont()
    //
    // Returns the font used to render the labels of the axis.
    //
    // -------------------------------------------------------------------------
    QFont labelFont() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::countMajorTicks()
    //
    // Returns the number of major ticks displayed in the axis.
    //
    // -------------------------------------------------------------------------
    int countMajorTicks() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::countMinorTicks()
    //
    // Returns the number of minor ticks displayed in the axis.
    //
    // -------------------------------------------------------------------------
    int countMinorTicks() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getZeroOffset()
    //
    // Returns the zero offset of the axis due to first major tick label.
    //
    // -------------------------------------------------------------------------
    int getZeroOffset() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getFrameSize()
    //
    // Returns the size of the axis frame. It is the full size of the widget
    // and comprises axis, labels and title size.
    //
    // -------------------------------------------------------------------------
    QSize getFrameSize();

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getMajorTickSize()
    //
    // Returns the size in pixels with double precision of a major tick.
    //
    // -------------------------------------------------------------------------
    double getMajorTickSize() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getMajorTickOffset()
    //
    // Returns the offset in pixels of the first major tick of the axis with
    // double precision.
    //
    // -------------------------------------------------------------------------
    double getMajorTickOffset() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getMajorTicksPosition()
    //
    // Returns a list with the positions of all major ticks of the axis
    // referred to axis zero.
    //
    // -------------------------------------------------------------------------
    QList<int> getMajorTicksPosition() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getMinorTickSize()
    //
    // Returns the size in pixels with double precision of a minor tick.
    //
    // -------------------------------------------------------------------------
    double getMinorTickSize() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getMinorTickOffset()
    //
    // Returns the offset in pixels of the first minor tick of the axis with
    // double precision.
    //
    // -------------------------------------------------------------------------
    double getMinorTickOffset() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getMinorTicksPosition()
    //
    // Returns a list with the positions of all minor ticks of the axis
    // referred to axis zero.
    //
    // -------------------------------------------------------------------------
    QList<int> getMinorTicksPosition() const;

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getValuePosition(double, bool*)
    //
    // Returns the position in pixels of a given value 'value' shown by the
    // axis. The position is referred to axis zero and 'in' boolean reference
    // stores true if the value is visible in the axis, else false.
    //
    // -------------------------------------------------------------------------
    int getValuePosition(double value, bool* in = 0);

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::getValuePosition(double, bool*)
    //
    // Returns the size in pixels of a given value range 'size' shown by the
    // axis. The 'in' boolean reference stores true if the range is fully
    // visible in the axis, else false.
    //
    // -------------------------------------------------------------------------
    int getValueRange(double size, bool* in = 0);


// == setters ==================================================================

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::setSize(int)
    //
    // The function sets the size of the axis. The aurgument 'size' cannot be
    // negative. The function returns true or false depending on assignment
    // success.
    //
    // -------------------------------------------------------------------------
    bool setSize(int size);

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::setMinimum(double)
    //
    // The function sets the minimum value hold by the axis. The aurgument 'min'
    // can be greater than maximum value but in this case these values will be
    // switched.
    //
    // -------------------------------------------------------------------------
    bool setMinimum(double min);

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::setMaximum(double)
    //
    // The function sets the maximum value hold by the axis. The aurgument 'max'
    // can be less than minimum value but in this case these values will be
    // switched.
    //
    // -------------------------------------------------------------------------
    bool setMaximum(double max);

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::setRange(double, double)
    //
    // The function sets the value range hold by the axis. The aurgument 'min'
    // and 'max' are assigned to minimum and maximum value of the axis based on
    // which one is greater than the other.
    //
    // -------------------------------------------------------------------------
    bool setRange(double min,
                  double max);

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::setRange(SDoubles*)
    //
    // The function sets the value range and increment of the axis based on
    // data contained in the SDoubles array pointed by 'array'. The increment
    // is set as a fourth of the array range while minumum and maximum values
    // are setted including two more interval ranges.
    //
    // -------------------------------------------------------------------------
    bool setRange(SDoubles* array);

    // -- public ---------------------------------------------------------------
    // StatS::SDoubleAxis::setIncrement(double)
    //
    // The function sets the value increment of the axis. Each major ticks of
    // the axis will be spaced by 'increment' value. The argument cannot be
    // null or less than zero. The function returns true of false based on
    // assignment result.
    //
    // -------------------------------------------------------------------------
    bool setIncrement(double increment);

    bool setPrecision(int precision);

    bool setFormat(char format);

    bool setMinorTicks(int num);

    bool setTitle(const QString title);

    bool setTitleFont(const QFont font);

    bool setAxisOrientation(Orientation axisOrientation);

    bool setLabelOrientation(Orientation axisOrientation);

    bool setLabelFont(const QFont font);

    bool setLabelsVisible(bool state);

    bool setMajorTicksVisible(bool state);

    bool setMajorGridVisible(bool state);

    bool setMinorTicksVisible(bool state);

    bool setMinorGridVisible(bool state);

// == inspectors ===============================================================

    bool isLabelsVisible() const;

    bool isMajorGridVisible() const;

    bool isMinorGridVisible() const;

    bool isMajorTicksVisible() const;

    bool isMinorTicksVisible() const;


// == accessor function declarations ===========================================

protected:

    void paintEvent(QPaintEvent* e);

    void mouseMoveEvent(QMouseEvent *e);

    void mousePressEvent(QMouseEvent *e);

    void mouseReleaseEvent(QMouseEvent *e);


// == variable declarations ====================================================

private:

    static const int TICKSIZE = 5;  // size of major ticks

    QString _title;                 // holds axis title
    double _min;                    // minumum value of the axis
    double _max;                    // maximum value of the axis
    double _increment;              // set increment for major ticks
    int _size;                      // size of the axis
    int _minTicks;                  // number of minor ticks    
    int _labelsPrecision;           // label precision
    char _labelsFormat;             // label format

    bool _labelsVisible;            // visible state of labels
    bool _minorTicksVisible;        // visible state of minor ticks
    bool _majorTicksVisible;        // visible state of major ticks
    bool _minorGridVisible;         // visible state of minor grid
    bool _majorGridVisible;         // visible state of major grid
    bool _reverseState;

    Orientation _axisOrient;        // orientation of the axis
    Orientation _labelOrientation;  // orientation of the labels
    QFont _labelsFont;              // font of the labels
    QFont _titleFont;               // font of the axis

    bool _keyState;
    QPoint _pressedPoint;

// == private function declarations ============================================

    void calculateFrameSize(int& lenght,
                            int& height);

    bool isInRightSizeHandle(const QPoint point);

private slots:

    void on_contextMenu(QPoint pos);

    void on_propertiesSetting();

    void on_sizeSetting();

    void on_titleSetting();

    void on_axisReversing();

signals:

    void propertiesChanged();

};

}

#endif // SDOUBLEAXIS_H
